/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.diagram.classdiagram.helper;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Component;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.ElementImport;
import org.eclipse.uml2.uml.Interface;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.PackageableElement;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.UMLFactory;

import com.cea.papyrus.core.helper.GraphEdgeHelper;
import com.cea.papyrus.core.helper.IGraphEdgeHelper;
import com.cea.papyrus.core.helper.NodeHelper;
import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.core.utils.VectorCalculus;
import com.cea.papyrus.diagraminterchange2.di2.Diagram;
import com.cea.papyrus.diagraminterchange2.di2.GraphConnector;
import com.cea.papyrus.diagraminterchange2.di2.GraphEdge;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;

/**
 * Helper for {@link Association}.
 */
public class EdgeAssociationHelper extends GraphEdgeHelper implements IGraphEdgeHelper{

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean canConnect(Element source,Element target){
		// target = null, this is the first step of connection (first anchor)
		if(target == null) {
			return checkSourceConnection(source);
		} else {
			// target is not null, this is either a refactoring or the 2nd part of the connection command
			// source AND target must be checked.
			return (checkSourceConnection(source) && checkTargetConnection(target));
		}
	}

	/**
	 * Checks if the specified element is a valid element as a target for an association connection
	 * @param source the element to check
	 * @return <code>true</code> if the specified element is not <code>null</code>, and is a class or an interface.
	 */
	protected boolean checkSourceConnection(Element source) {
		if(source != null) {
			if(source instanceof org.eclipse.uml2.uml.Class) {
				return true;
			} else if(source instanceof Interface) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Checks if the specified element is a valid element as a source for an association connection
	 * @param target the element to check
	 * @return <code>true</code> if the specified element is not <code>null</code>, and is a class or an interface.
	 */
	protected boolean checkTargetConnection(Element target) {
		if(target != null) {
			if(target instanceof org.eclipse.uml2.uml.Class) {
				return true;
			} else if(target instanceof Interface) {
				return true;
			} else if(target instanceof ElementImport) {
				// imported element can be a class or a interface
				if( ((ElementImport)target).getImportedElement() instanceof Class) {
					return true;
				} else if (((ElementImport)target).getImportedElement() instanceof Interface){
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean canCreateGraphElement(GraphElement source){
		Element eltSource=null;
		/*if (source.getSemanticModel()!=null ){
			if (((Uml1SemanticModelBridge)source.getSemanticModel()).getElement()!=null){
				eltSource=((Uml1SemanticModelBridge)source.getSemanticModel()).getElement();
				if(eltSource instanceof org.eclipse.uml2.uml.Stereotype)
					return false;
				else if ((eltSource instanceof  org.eclipse.uml2.uml.Class)||
						(eltSource instanceof  org.eclipse.uml2.uml.Interface)/*||
						(eltSource instanceof  org.eclipse.uml2.uml.Stereotype)*//*){// they are  classifiers
					return true;
				}
			}
		}*/
		if ((source instanceof Diagram)  ){
			if (((Uml1SemanticModelBridge)((Diagram)source).getOwner()).getElement()!=null){
				eltSource=((Uml1SemanticModelBridge)((Diagram)source).getOwner()).getElement();
				if (eltSource instanceof Component || eltSource instanceof Package){return true;}
			}
		}
		else if (source.getSemanticModel()!=null ){
			if (((Uml1SemanticModelBridge)source.getSemanticModel()).getElement()!=null){
				eltSource=((Uml1SemanticModelBridge)source.getSemanticModel()).getElement();
				if (eltSource instanceof Component ||eltSource instanceof Package){return true;}
			}
		}
		return false;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ArrayList createGraphEdge(Element umlElement, Diagram diagram){
		Debug.debug(this,"createGraphEdge", 10);
		GraphEdge ge=(GraphEdge)this.createGraphElement(umlElement, null, null, null);

		//look for UML connected UML element
		if (!(umlElement instanceof Association)){
			Debug.error(this, "EdgeAssociationFactory.createGraphEdge: "+ umlElement+ "is not an association!", channel);
		}
		Association de=(Association)umlElement;

		Element umlsupplier=(Element)de.getEndTypes().get(0);
		Element umlsource=(Element)de.getEndTypes().get(1);
		super.endCreateGraphElement(umlsource, umlsupplier, ge, diagram);
		return new ArrayList();
	}
	/**
	 * {@inheritDoc}
	 */
	protected void finalizeEndCreateGraphElement(GraphEdge edge, GraphNode source, GraphNode target){
		
		edge.getAnchor().get(0).eAdapters().clear();
		edge.getAnchor().get(1).eAdapters().clear();
		GraphConnector first= edge.getAnchor().get(0);
		GraphConnector second= edge.getAnchor().get(1);
		GraphElement container1= first.getGraphElement();
		GraphElement container2=second.getGraphElement();
		
		//deconnect
		first.setGraphElement(null);
		second.setGraphElement(null);
		edge.setContainer(null);
		
		//add with good position for connectors
		edge.getAnchor().clear();
		edge.getAnchor().add(second);
		edge.getAnchor().add(first);
		first.setGraphElement(container1);
		second.setGraphElement(container2);
		VectorCalculus.setGoodPositionForAssociation(edge,(GraphNode)target,(GraphNode)source);
		super.finalizeEndCreateGraphElement(edge, source, target);
	}



	/**
	 * {@inheritDoc}
	 */
	@Override
	public GraphElement createGraphElement(Element umlChild, GraphElement parent, Point location, Dimension size){

		GraphElement ge=super.createGraphElement(umlChild, null, location, size);
		NodeHelper nf=new NodeHelper();

		// name of Association
		GraphElement graphNode=nf.createGraphElement(null, ge, new Point(0, 10), null);
		graphNode.setFontSize(9);
		graphNode.setIsVisible(false);

		// Role of AssociationEnd : target Role
		graphNode=nf.createGraphElement(null,ge,new Point(0, 10),null);
		graphNode.setFontSize(9);

		// Role of AssociationEnd : sourceRole
		graphNode=nf.createGraphElement(null,ge,new Point(0, 10),null);
		graphNode.setFontSize(9);
		graphNode.setIsVisible(false);

		// multiplicity of AssociationEnd : target Multiplicity
		graphNode=nf.createGraphElement(null,ge,new Point(0, -15),null);
		graphNode.setFontSize(9);


		// multiplicity of AssociationEnd : source Multiplicity
		graphNode=nf.createGraphElement(null,ge,new Point(0, -15),null);
		graphNode.setFontSize(9);
		graphNode.setIsVisible(false);

		// stereotype of Association
		graphNode=nf.createGraphElement(null,ge,new Point(0, 30),null);

		//it can be sent an event so it have to be done in last
		if(parent!=null){
			parent.getContained().add(ge);
		}
		return ge;		
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public org.eclipse.uml2.uml.Element createUMLElement(String name, org.eclipse.uml2.uml.Element umlParent){
		Debug.debug(this,"create association", channel); //$NON-NLS-1$
		Association as= UMLFactory.eINSTANCE.createAssociation();
		as.setName(name);
		((Package)umlParent.getNearestPackage()).getPackagedElements().add(as);
		return as;		
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.helper.GraphEdgeHelper#canCreateUMLElement(org.eclipse.uml2.uml.Element)
	 */
	@Override
	public boolean canCreateUMLElement(Element umlParent) {
		if(umlParent instanceof Package) {
			return true;
		}
		return false;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean addUMLElement(Element element, Element umlParent) {
		if(umlParent instanceof Package) {
			return ((Package)umlParent).getPackagedElements().add((PackageableElement)element);
		}
		return false;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean removeUMLElement(Element element, Element umlParent) {
		if(umlParent instanceof Package) {
			return ((Package)umlParent).getPackagedElements().remove(element);
		}
		return false;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean deconnect(Element source, Element target, Element link) {
		Debug.debug(this,"   Detect Association link", channel);

		((Property)source).setAssociation(null);
		((Property)target).setAssociation(null);

		/*
		Association association=(Association)link;		
		association.setName(getNewUMLElementName(link.getOwner()));
		//creation of an attribute for each class
		Classifier cSource=(Classifier)source;
		Classifier cTarget=(Classifier)target;

		//look for property associated in Csource
		Property att=null;
		Iterator it= cSource.getAllAttributes().iterator();
		while (it.hasNext()){
			Property temp= (Property)it.next();
			if (temp.getAssociation().equals(link)){
				att=temp;
			}
		}
		if(att!=null){
			att.setType(null);
			((Class)cSource).getOwnedAttributes().remove(att);
		}
		//look for property associated in cTarget
		att=null;
		it= cTarget.getAllAttributes().iterator();
		while (it.hasNext()){
			Property temp= (Property)it.next();
			if (temp.getAssociation().equals(link)){
				att=temp;
			}
		}
		if(att!=null){
			att.setType(null);
			((Class)cTarget).getOwnedAttributes().remove(att);
		}

		//	look for property of Association
		ArrayList attList=new ArrayList();
		it= ((Association)link).getAllAttributes().iterator();
		while (it.hasNext()){
			Property temp= (Property)it.next();
			if (temp.getAssociation().equals(target)||temp.getAssociation().equals(source)){
				attList.add(temp);
			}
		}
		if(attList.size()!=0){
			it =attList.iterator();
			while(it.hasNext()){
				att=(Property)it.next();
				att.setType(null);
				((Association)link).getOwnedEnds().remove(att);
			}
		}
		 */
		return true;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean connect(Element source, Element target, Element link) {
		Debug.debug(this,"   Detect Association link", channel);

		Association association=(Association)link;		
		association.setName(getBaseString()+((Property)source).getType().getName()+ "_"+ ((Property)target).getType().getName());


		// ((Property)target).setAssociation(association);
		// ((Property)source).setAssociation(association);
		List memberEnds = association.getMemberEnds();

		if((memberEnds.indexOf(((Property)target)) >=0)) {
			association.getMemberEnds().move(0, ((Property)target));
		} else {
			association.getMemberEnds().add(0, ((Property)target));
		}


		if((memberEnds.indexOf(((Property)source)) >=0)) {
			association.getMemberEnds().move(1, ((Property)source));
		} else {
			association.getMemberEnds().add(1, ((Property)source));
		}

		return true;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String getBaseString() {
		return "Association_"; //$NON-NLS-1$
	}
}
